<?php

namespace App\Services\WxMaterial;

use App\Models\WxMaterial;
use App\Models\WxMaterialNew;
use App\Models\WxMaterialNewImg;
use App\Services\EasyWechat;
use App\Services\SysFileService;
use App\Services\WxErrorService;
use EasyWeChat\Kernel\Messages\Article;
use EasyWeChat\Kernel\Messages\News;
use EasyWeChat\Kernel\Messages\NewsItem;

/**
 * 图文素材业务逻辑
 * @package App\Services\WxMaterial
 */
class WxMaterialNews implements WxMaterialInterface
{
    /**
     * 保存素材数据
     * @param $data
     */
    public function save($data)
    {
        \DB::beginTransaction();
        try {
            $materialData = $data['material_data'];
            $newsList = $data['news_list'];
            if (!empty($materialData['material_id'])) {
                $findMaterial = WxMaterial::find($materialData['material_id']);
            } else {
                $findMaterial = new WxMaterial;
            }
            $findMaterial->cat_id = $materialData['cat_id'];
            $findMaterial->type = 'news';
            $findMaterial->is_forever = $materialData['is_forever'];
            $title = '';
            $description = [];
            $previewData = [];
            if ($newsList) {
                foreach ($newsList as $v) {
                    $title || $title = $v['title'];
                    $description[] = $v['title'];
                    $previewData[] = [
                        'title' => $v['title'],
                        'thumb_view_url' => $v['thumb_view_url'],
                        'digest' => empty($v['digest']) ? mb_substr(strip_tags($v['content']), 0, 64, 'utf-8') : $v['digest'],
                    ];
                }
            }
            $findMaterial->title = $title;
            $findMaterial->description = implode('|', $description);
            $findMaterial->preview_data = $previewData;
            $findMaterial->setUpdatedAt(time());
            $findMaterial->save();
            $materialId = $findMaterial['material_id'] ?? 0;
            if (empty($materialId)) {
                throw new \Exception('生成素材ID失败');
            }
            //保存文章列表
            if (!empty($newsList)) {
                WxMaterialNew::where('material_id', $materialId)->delete();
                foreach ($newsList as $k => $v) {
                    if (!empty($v['new_id'])) {
                        $findNew = WxMaterialNew::withTrashed()->findOrFail($v['new_id']);
                    } else {
                        $findNew = new WxMaterialNew;
                    }
                    $findNew->material_id = $materialId;
                    $findNew->title = $v['title'] ?? '';
                    $findNew->thumb_view_url = $v['thumb_view_url'] ?? '';
                    $findNew->author = $v['author'] ?? '';
                    $findNew->digest = empty($v['digest']) ? mb_substr(strip_tags($v['content']), 0, 64, 'utf-8') : $v['digest'];
                    $findNew->show_cover_pic = $v['show_cover_pic'] ?? 0;
                    $findNew->content = $v['content'];
                    $findNew->content_source_url = $v['content_source_url'] ?? '';
                    $findNew->news_click_url = $v['news_click_url'] ?? '';
                    $findNew->need_open_comment = $v['need_open_comment'] ?? 0;
                    $findNew->only_fans_can_comment = $v['only_fans_can_comment'];
                    $findNew->order_num = $k;
                    $findNew->save();
                    $findNew->restore();
                }
            }

            \DB::commit();
        } catch (\Exception $e) {
            \DB::rollBack();
            throw new \Exception($e->getMessage());
        }
        //永久素材则异步获取mediaId
        if ($findMaterial->is_forever) {
            \App\Jobs\WxMaterialNewsUpload::dispatch($materialId)->onQueue('low');
        }
        return true;
    }

    /**
     * 删除素材
     * @param $materialId
     */
    public function del($materialId)
    {
        $findMaterial = WxMaterial::find($materialId);
        \DB::beginTransaction();
        try {
            if (!empty($findMaterial->media_id)) {
                $app = EasyWechat::officialAccount();
                if ($findMaterial->is_forever) {
                    $apiRs = $app->material->delete($findMaterial->media_id);
                    WxErrorService::checkApiResponse($apiRs);
                }
            }
            $findMaterial->media_id = '';
            $findMaterial->expire_time = null;
            $findMaterial->save();
            $findMaterial->delete();
            \DB::commit();
        } catch (\Exception $e) {
            \DB::rollBack();
            throw new \Exception($e->getMessage());
        }
        return true;
    }

    /**
     * 获取被动回复数据
     * @param $materialId
     */
    public function getPassiveReplyData($materialId)
    {
        $findMaterial = WxMaterial::find($materialId);
        $newsItems = [];
        if ($findMaterial->news) {
            foreach ($findMaterial->news as $index => $newVal) {
                $newsItems[] = new NewsItem([
                    'title' => $newVal->title ?? '',
                    'description' => $newVal->digest ?? '',
                    'url' => !empty($newVal->news_click_url) ? $newVal->news_click_url : route('wechat.news.view', ['new_id' => $newVal->new_id]),
                    'image' => $newVal->thumb_view_url ?? '',
                ]);
            }
        }
        $news = new News($newsItems);
        return $news;
    }

    /**
     * 获取微信mediaId，只有永久素材才能生成mediaId
     * @param $materialId 素材主键ID
     * @param $reUpload 是否重新上传
     * @return string 素材media_id
     */
    public function getMediaId($materialId, $reUpload = false)
    {
        $findMaterial = WxMaterial::findOrFail($materialId);
        if ($findMaterial->is_forever == 0) {
            throw new \Exception('必须是永久素材才能获取mediaId');
        }
        if (empty($findMaterial['media_id']) || $reUpload) {
            \DB::beginTransaction();
            try {
                $findMaterial->push_result = '';
                $app = EasyWechat::officialAccount();
                if (empty($findMaterial->news)) {
                    throw new \Exception('图文列表不能为空');
                }
                $thumbService = new WxMaterialThumb();
                $articleList = [];
                foreach ($findMaterial->news as $new) {
                    //永久素材上传封面图
                    $thumbData = $thumbService->save([
                        'title' => $new['title'] . '-封面图',
                        'is_forever' => $findMaterial->is_forever,
                        'view_url' => $new['thumb_view_url'],
                    ]);
                    $new->thumb_material_id = $thumbData['material_id'] ?? '';
                    $new->thumb_media_id = $thumbData['media_id'];
                    $new->save();
                    //组装图文数据
                    $articleList[] = new Article([
                        'title' => $new->title,//标题
                        'thumb_media_id' => $new->thumb_media_id,//图文消息的封面图片素材id
                        'show_cover' => $new->show_cover_pic ?? 0,//是否显示封面
                        'author' => $new->author ?? '',//作者
                        'digest' => empty($new->digest) ? mb_substr(strip_tags($new->content), 0, 64, 'utf-8') : $new->digest,
                        'content' => $this->replaceContentWxImg($new->content),//图文消息的具体内容
                        'source_url' => $new->content_source_url,//图文消息的原文地址
                        'need_open_comment' => $new->need_open_comment ?? 0,//是否打开评论
                        'only_fans_can_comment' => $new->only_fans_can_comment ?? 0,//是否粉丝才可评论
                    ]);
                }
                //上传图文
                if (!empty($findMaterial->media_id)) {//更新素材
                    foreach ($articleList as $index => $article) {
                        $apiRs = $app->material->updateArticle($findMaterial->media_id, $article, $index);
                        try{
                            WxErrorService::checkApiResponse($apiRs);
                        }catch (\Exception $e){
                            $findMaterial->push_result = $e->getMessage();
                        }
                    }
                } else {
                    $apiRs = $app->material->uploadArticle($articleList);
                    try{
                        WxErrorService::checkApiResponse($apiRs);
                    }catch (\Exception $e){
                        $findMaterial->push_result = $e->getMessage();
                    }
                    $findMaterial->media_id = $apiRs['media_id'] ?? '';
                }
                $findMaterial->push_time = time();
                $findMaterial->save();
                \DB::commit();
            } catch (\Exception $e) {
                \DB::rollBack();
                throw new \Exception($e->getMessage());
            }
        }
        return $findMaterial->media_id;
    }

    /**
     * 上传群发图文素材（现在群发都用永久图文素材，easywechat官方api也不支持该种方式，这里只是扩展一下功能）
     * @param $articles
     * @return mixed
     */
    protected function uploadMassArticle($articles)
    {
        if ($articles instanceof Article || !empty($articles['title'])) {
            $articles = [$articles];
        }

        $params = ['articles' => array_map(function ($article) {
            if ($article instanceof Article) {
                return $article->transformForJsonRequestWithoutType();
            }

            return $article;
        }, $articles)];
        $app = EasyWechat::officialAccount();
        return $app->base->httpPostJson('cgi-bin/media/uploadnews', $params);
    }
    /**
     * 获取群发素材ID
     * @param $materialId
     * @param $reUpload
     */
    public function getMassMediaId($materialId, $reUpload = false)
    {
        $findMaterial = WxMaterial::findOrFail($materialId);
        if (empty($findMaterial['mass_media_id']) || $reUpload) {
            \DB::beginTransaction();
            try {
                if (empty($findMaterial->news)) {
                    throw new \Exception('图文列表不能为空');
                }
                $thumbService = new WxMaterialThumb();
                $articleList = [];
                foreach ($findMaterial->news as $new) {
                    //使用临时素材接口上传封面图
                    $thumbData = $thumbService->save([
                        'title' => $new['title'] . '-封面图',
                        'is_forever' => 1,
                        'view_url' => $new['thumb_view_url'],
                    ]);
                    $new->thumb_material_id = $thumbData['material_id'] ?? '';
                    $new->thumb_media_id = $thumbData['media_id'];
                    $new->save();
                    //组装图文数据
                    $articleList[] = new Article([
                        'title' => $new->title,//标题
                        'thumb_media_id' => $new->thumb_media_id,//图文消息的封面图片素材id
                        'show_cover' => $new->show_cover_pic ?? 0,//是否显示封面
                        'author' => $new->author ?? '',//作者
                        'digest' => empty($new->digest) ? mb_substr(strip_tags($new->content), 0, 64, 'utf-8') : $new->digest,
                        'content' => $this->replaceContentWxImg($new->content),//图文消息的具体内容
                        'source_url' => $new->content_source_url,//图文消息的原文地址
                        'need_open_comment' => $new->need_open_comment ?? 0,//是否打开评论
                        'only_fans_can_comment' => $new->only_fans_can_comment ?? 0,//是否粉丝才可评论
                    ]);
                }
                //上传图文
                $apiRs = $this->uploadMassArticle($articleList);
                WxErrorService::checkApiResponse($apiRs);
                $findMaterial->mass_media_id = $apiRs['media_id'] ?? '';
                $findMaterial->save();
                \DB::commit();
            } catch (\Exception $e) {
                \DB::rollBack();
                throw new \Exception($e->getMessage());
            }
        }
        return $findMaterial->mass_media_id;
    }
    /**
     * 替换图文内容中的图片为微信图片地址
     * @param $content
     * @return mixed
     * @throws \Exception
     */
    private function replaceContentWxImg($content)
    {
        $pattern = '/<img.*?src="([^"]*?)".*?/';
        preg_match_all($pattern, $content, $arr);
        $imgList = $arr[1];
        $wxImgList = [];
        //批量上传到微信
        foreach ($imgList as $imgUrl) {
            //上传图片到微信端
            $wxImgUrl = $this->getWxViewUrl($imgUrl);
            //重试一次
            if (empty($wxImgUrl)) {
                $wxImgUrl = $this->getWxViewUrl($imgUrl);
            }
            $wxImgList[] = $wxImgUrl;
        }
        $content = str_replace($imgList, $wxImgList, $content);
        return $content;
    }

    /**
     * 提供图片访问地址转换成微信图片访问地址
     * @param $viewUrl
     * @return mixed
     * @throws \Exception
     */
    public function getWxViewUrl($viewUrl)
    {
        $findNewImg = WxMaterialNewImg::where('view_url', $viewUrl)->first();
        if (empty($findNewImg)) {
            $app = EasyWechat::officialAccount();
            //拉取远程图片到本地
            $sysFileSer = new SysFileService();
            $localFileInfo = $sysFileSer->getLocalFileInfoByRemoteUrl($viewUrl);

            $apiRs = $app->material->uploadArticleImage($localFileInfo['local_file_path']);
            WxErrorService::checkApiResponse($apiRs);
            $findNewImg = new WxMaterialNewImg;
            $findNewImg->view_url = $viewUrl;
            $findNewImg->wx_view_url = $apiRs['url'];
            $findNewImg->save();
        }
        return $findNewImg->wx_view_url;
    }

    /**
     * 下载单个图文素材到本地
     * @param $mediaId
     * @param bool $isForever
     */
    public function downloadMediaId($mediaId, $isForever = true)
    {
        if (!$isForever){
            throw new \Exception('不支持临时图文素材拉取');
        }
        $app = EasyWechat::officialAccount();
        $apiRs = $app->material->get($mediaId);
        WxErrorService::checkApiResponse($apiRs);
        if (empty($apiRs['news_item'])){
            throw new \Exception('图文素材的文章列表为空');
        }
        try{
            return $this->syncSave($mediaId, $apiRs['news_item']);
        }catch (\Exception $e){
            \Log::info('素材拉取失败：'.$e->getMessage(), array_merge(['media_id'=>$mediaId], $apiRs['news_item']));
        }
    }

    /**
     * 同步线上数据到本地数据
     */
    public function sync()
    {
        $pageOffset = 0;
        $pageSize = 20;
        $total = 0;
        $itemPageCount = 0;//当前总数
        $itemTotalCount = 0;//该类别总数
        $app = EasyWechat::officialAccount();
        do{
            $list = $app->material->list('news', $pageOffset, $pageSize);
            WxErrorService::checkApiResponse($list);
            if (!empty($list['item'])){
                foreach ($list['item'] as $k => $v){
                    if (!empty($v['media_id']) && $v['content']['news_item']){
                        //$this->syncSave($v['media_id'], $v['content']['news_item']);
                        \App\Jobs\WxMaterialNewsDownloadSingle::dispatch($v['media_id'], $v['content']['news_item'])->onQueue('low');
                    }
                }
            }
            $itemPageCount = $list['item_count'] ?? 0;
            $itemTotalCount = $list['total_count'] ?? 0;
            $pageOffset += $itemPageCount;
            $total += $itemPageCount;
        }while($itemPageCount == $pageSize && $total < $itemTotalCount);
        return true;
    }

    /**
     * 同步图文素材数据处理
     * @param $mediaId
     * @param array $newsItem
     */
    public function syncSave($mediaId, $newsItem = [])
    {
        $findMaterial = WxMaterial::where('media_id', $mediaId)->first();
        $sysFile = new SysFileService();
        \DB::beginTransaction();
        try{
            if ($findMaterial){
                //先删除现有图文
                WxMaterialNew::where('material_id', $findMaterial->material_id)->delete();
            }else{
                $findMaterial = new WxMaterial;
                $findMaterial->cat_id = 0;
            }
            $findMaterial->type = 'news';
            $findMaterial->is_forever = 1;
            $findMaterial->media_id = $mediaId;
            //先保存空数据为了生成素材主键ID
            $findMaterial->save();
            $materialId = $findMaterial->material_id ?? 0;
            if (empty($materialId)){
                throw new \Exception('素材ID生成失败');
            }
            //收集素材标题、描述、预览数据等
            $title = '';
            $description = [];
            $previewData = [];
            $thumbSer = new WxMaterialThumb();
            foreach ($newsItem as $k => $v) {
                $findNew = WxMaterialNew::withTrashed()->where('material_id', $materialId)->where('order_num', $k)->first();
                if (empty($findNew)) {
                    $findNew = new WxMaterialNew;
                }
                $findNew->material_id = $materialId;
                $findNew->title = $v['title'] ?? '';
                $findNew->thumb_media_id = $v['thumb_media_id'];
                //根据微信素材ID获取本地素材库相关信息
                try{
                    $thumbMaterialData = $thumbSer->downloadMediaId($findNew->thumb_media_id);
                }catch (\Exception $e){
                    \Log::error('通过mediaId获取临时缩略图失败:' . $e->getMessage(), ['thumb_media_id' => $v['thumb_media_id']]);
                }
                if (empty($thumbMaterialData)) {
                    try {
                        $thumbMaterialData = $thumbSer->downloadMediaId($findNew->thumb_media_id, true);
                    } catch (\Exception $e) {
                        \Log::error('通过mediaId获取永久缩略图失败:' . $e->getMessage(), ['thumb_media_id' => $v['thumb_media_id']]);
                    }
                }
                //如果通过mediaId获取失败改为通过缩略图地址获取
                if ( empty($thumbMaterialData) && !empty($v['thumb_url'])){
                    try{
                        //传入微信图片地址获取自由云地址来获得素材mediaId，这种方式可能造成重复的缩略图mediaId
                        $localFileInfo = $sysFile->getLocalFileInfoByRemoteUrl($v['thumb_url']);
                        $thumbMaterialData = $thumbSer->save(['title'=>$v['title'].'-封面图','is_forever'=>1,'view_url'=>$localFileInfo['view_url'], 'wx_view_url' => $v['thumb_url'], 'media_id' => $findNew->thumb_media_id]);
                        //替换掉报错不能使用的缩略图素材ID
                        $findNew->thumb_media_id = $thumbMaterialData['media_id'] ?? '';
                    }catch (\Exception $e){
                        \Log::error('通过微信端缩略图地址拉取缩略图失败:'. $e->getMessage(), [$v['thumb_url']]);
                    }
                }
                $findNew->thumb_material_id = $thumbMaterialData['material_id'] ?? '';
                $findNew->thumb_view_url = $thumbMaterialData['view_url'] ?? '';
                $findNew->author = $v['author'] ?? '';
                $findNew->digest = empty($v['digest']) ? mb_substr(strip_tags($v['content']), 0, 64, 'utf-8') : $v['digest'];
                $findNew->show_cover_pic = $v['show_cover_pic'] ?? 0;
                $findNew->content = $this->replaceContentSelfImg($v['content']);
                $findNew->content_source_url = $v['content_source_url'] ?? '';
                $findNew->news_click_url = $v['url'] ?? '';
                $findNew->need_open_comment = $v['need_open_comment'] ?? 0;
                $findNew->only_fans_can_comment = $v['only_fans_can_comment'] ?? 0;
                $findNew->order_num = $k;
                $findNew->save();
                $findNew->restore();
                $title || $title = $findNew->title;
                $description[] = $findNew->title;
                $previewData[] = [
                    'title' => $findNew->title,
                    'thumb_view_url' => $findNew->thumb_view_url,
                    'digest' => $findNew->digest,
                ];
            }
            //重新保存素材数据
            $findMaterial->title = $title;
            $findMaterial->description = implode('|', $description);
            $findMaterial->preview_data = $previewData;
            $findMaterial->setUpdatedAt(time());
            $findMaterial->push_time = time();
            $findMaterial->expire_time = null;
            $findMaterial->save();

            \DB::commit();
        } catch (\Exception $e) {
            \DB::rollBack();
            \Log::error($e->getMessage(), ['mediaId' => $mediaId,'newItem'=>$newsItem]);
            throw new \Exception($e->getMessage());
        }

    }

    /**
     * 替换图文内容中的图片为自有图片地址
     * @param $content
     * @return mixed
     * @throws \Exception
     */
    public function replaceContentSelfImg($content)
    {
        $pattern = '/<img.*?src="([^"]*?)".*?/';
        preg_match_all($pattern, $content, $arr);
        $imgList = $arr[1];
        $selfImgList = [];
        //批量上传到微信
        foreach ($imgList as $imgUrl) {
            //上传图片到微信端
            $wxImgUrl = $this->getSelfViewUrl($imgUrl);
            //重试一次
            if (empty($wxImgUrl)) {
                $wxImgUrl = $this->getSelfViewUrl($imgUrl);
            }
            $selfImgList[] = $wxImgUrl;
        }
        $content = str_replace($imgList, $selfImgList, $content);
        return $content;
    }

    /**
     * 将微信端图片拉取到本地数据库中
     * @param $wxViewUrl
     * @return mixed
     */
    public function getSelfViewUrl($wxViewUrl)
    {
        $findNewImg = WxMaterialNewImg::where('wx_view_url', $wxViewUrl)->first();
        if (empty($findNewImg)) {
            //拉取微信端图片到本地
            $sysFileSer = new SysFileService();
            try{
                $localFileInfo = $sysFileSer->getLocalFileInfoByRemoteUrl($wxViewUrl);
            }catch (\Exception $e){
                \Log::error('下载微信图片失败:'. $e->getMessage());
            }
            $findNewImg = new WxMaterialNewImg;
            $findNewImg->view_url = !empty($localFileInfo['view_url']) ? $localFileInfo['view_url'] : $wxViewUrl;
            $findNewImg->wx_view_url = $wxViewUrl;
            $findNewImg->save();
        }
        return !empty($findNewImg->view_url) ? $findNewImg->view_url : '';
    }
}